<template>
    <section v-if="config" style="position: relative;"
             :class="{'preview-mode':preview}">
        <p v-if="usePageTitle && config.pageTitle" class="wrapper-title">
            <span>{{config.pageTitle}}</span>
            <!-- 其它 -->
        </p>
        <div class="content-wrapper">
            <el-form ref="form" :model="model" v-bind="config.form.props" :style="config.form.style">
                <fieldset :disabled="preview">
                    <!-- parts -->
                    <template v-for="part in config.parts">
                        <p v-if="part.title" class="mb-20">
                            {{part.title}}
                        </p>
                        <DynamicComponentsRender :model="model" :components="part.fields"/>
                    </template>

                    <!-- footer -->
                    <el-form-item v-if="!(preview || design)">
                        <template v-for="item in config.operates" v-if="item.type">
                            <component :is="item.type" v-bind="item.props" v-on="handleCustomEvents(item.listeners)">
                                {{item.text}}
                            </component>
                        </template>
                        <el-button @click="onCancel">
                            {{config.cancel && config.cancel.text || '取消'}}
                        </el-button>
                    </el-form-item>
                </fieldset>
            </el-form>
        </div>
    </section>
</template>

<script>
    /**
     * - tag.vue问题
     * */
    import ElementUI from 'element-ui'
    import bus from 'components/tag/bus'
    import DynamicComponentsRender from './DynamicComponentsRender'
    import dict from './dict'
    import {HighElFormItem} from './hoc'

    const EL_FORM_ITEM_NAME = HighElFormItem.name

    const parseUrl = function (o) {
        // url": 如何有url 则url会自动获取数据覆盖list内的数据 {method:'方法',module:'模块',value:'模块下具体路径'}此请求需对应 api内文件
        return this.api[o.module][o.value] || ''
    }
    const getDataPathByPath = function (obj, path) {
        if (!path) return obj
        path = path.split('.')
        let i = 0
        while (i < path.length) {
            obj = obj[path[i++]]
        }
        return obj
    }
    const getParentByName = function (parentName) {
        let parent = this.$parent
        while (parent.$options.name !== parentName) {
            if (!parent.$parent) return null
            parent = parent.$parent
        }
        return parent
    }

    const dispatch = function (parentName, parentEventName, data) {
        let parent = getParentByName.call(this, parentName)
        parent && parent.$emit(parentEventName, data)
    }

    const getFormItem = function (vm) {
        return getParentByName.call(vm, EL_FORM_ITEM_NAME)
    }
    // 更方便使用render函数
    const utils = {
        validate(val) {
            dispatch.call(this, EL_FORM_ITEM_NAME, 'el.form.change', val)
        },
        setRule(rule) {
            const elFormItem = getFormItem(this)
            elFormItem && elFormItem.setCustomRules(rule)
        },
        clearValidate() {
            const elFormItem = getFormItem(this)
            elFormItem && elFormItem.clearValidate()
        },
        resetField() {
            const elFormItem = getFormItem(this)
            elFormItem && elFormItem.resetField()
        },
    }
    const utilsEntry = function (uName, ...args) {
        typeof utils[uName] === 'function' && utils[uName].call(this, ...args)
    }


    let $_SYS = null
    export default {
        // name设置为与index.js相同
        name: 'DynamicForm',
        components: {
            DynamicComponentsRender,
        },
        provide() {
            return {
                version: this.version,
                dForm: this, // 可以修复
                utils: utilsEntry,
                dict: this.dict,
                getDataPathByPath: this.getDataPathByPath,
                parseUrl: this.parseUrl,
            }
        },
        props: {
            // 页头
            usePageTitle: {
                type: Boolean,
                default: false,
            },

            // 预览模式
            preview: {
                type: Boolean,
                default: false,
            },

            // 设计模式
            design: {
                type: Boolean,
                default: false,
            },

            /* 如果编辑模式，必须成对出现，不判断了，一般也用不到 */
            // 内联模式，同步
            _config: Object,
            _model: Object,
            // 内联模式，异步
            _cid: [String, Number],
            _mid: [String, Number],
        },
        data() {
            Object.assign(this, {
                version: ElementUI.version,
                dict,
                getDataPathByPath,
                parseUrl,
            })
            this.isCreateMode = false
            this.sys = null
            this.config = null

            this.cid = this._cid
            this.mid = this._mid
            return {
                model: null, // or formData
            }
        },
        methods: {
            handleCustomEvents(evtMap) {
                // 需保证值都是函数，免去判断
                let isH = /^bound/
                for (let k in evtMap) {
                    if (isH.test(evtMap[k].name)) continue
                    evtMap[k] = evtMap[k].bind(this)
                }

                return evtMap
            },
            getSys() {
                return $_SYS || (() => {
                    'use strict'
                    return this.http.get('/static/dynamic-form-config.js').then(file => {
                        const exports = $_SYS = {}
                        eval(file)
                        return exports
                    })
                })()
            },
            setUrlParams() {
                if (this._config || this._cid) return
                /**
                 * 路由参数说明
                 * dynamicForm/:cid/:mid?
                 * cid，configId，表单配置ID
                 * mid，formId，表单提交之后的数据ID，然后获取数据，然后从数据获取cid（url也有cid，如果数据也有cid的话，以数据为准）
                 *
                 * 每加一个参数拷贝一次，
                 * 必选参数，\/([^\/\?]*)，
                 * 非必选参数，(\/([^\/\?]*))?，同时index+1
                 * */

                /**
                 // /.*dynamicForm\/([^\/\?]*)/
                 // http://localhost:8281/#/home/dynamicForm/12123
                 // http://localhost:8281/#/home/dynamicForm/12123?a=2
                 // http://localhost:8281/#/home/dynamicForm/12123/12?a=2

                 // /.*dynamicForm\/([^\/\?]*)(\/([^\/\?]*))?/
                 // http://localhost:8281/#/home/dynamicForm/121/23
                 // http://localhost:8281/#/home/dynamicForm/121/23?a
                 // http://localhost:8281/#/home/dynamicForm/121/23/1233?a
                 */

                    // params
                const m = this.$route.fullPath.match(/.*dynamicForm\/([^\/\?]*)/) /* 其它参数 */
                if (m) {
                    this.cid = m[1]
                    /* 其它参数 */
                }

                // query
                this.mid = this.$route.query.id
            },
            setState() {
                this.isCreateMode = !this.mid
            },
            getFormData() {
                const url = this.config.modelUrl

                // 如果没有设置数据从哪里获取，则返回所有query
                if (!url) {
                    return this.$route.query
                }

                return this.http[url.method](this.parseUrl(url), {id: this.mid}).then(res => {
                    return res.data
                })
            },
            getDefaultModelValueByConfig(config) {
                switch (config.type) {
                    case dict.number:
                        return undefined
                    case dict.select:
                        if (config.props && config.props.multiple) return []
                        return ''
                    case dict.radio:
                        return ''
                    case dict.checkbox:
                        return []
                    case dict.uploadImg:
                        return ''
                    case dict.uploadImgs:
                        return []
                    case dict.switch:
                        return 0
                    default:
                        return undefined // 由组件自适应
                }
            },
            genModelStructByConfig(model) {
                model = model || {} // 注意不要使用es6语法
                const {config} = this
                if (!config.parts) return model // 无
                const main = (model, config) => {
                    // if (!config.fields) return  // 无 将判断写在外部，优化性能
                    config.fields.forEach(item => {
                        if (!item.model) return // 无
                        if (model.hasOwnProperty(item.model)) return // 数据回显
                        if (item.fields) {
                            // 此处不处理，由DynamicComponentsRender.vue处理
                            // if (item.scope) {
                            //     model[item.model] = {}
                            //     main(model[item.model], item)
                            // } else {
                            //     main(model, item)
                            // }
                            return
                        }
                        if (item.component) {
                            model[item.model] = this.getDefaultModelValueByConfig(item.component)
                            return
                        }
                        // 渲染函数、或者其他的
                        model[item.model] = undefined
                    })
                }
                config.parts.forEach(part => {
                    part.fields && main(model, part)
                })
                return model
            },
            getRawConfig() { /* 成功true 失败false */
                return new Promise(resolve => {
                    const mockData = require('./mock/c')
                    if (!mockData[this.cid]) return resolve(null)
                    resolve(mockData[this.cid])
                })
            },
            genConfig(config) {
                const {dict} = this
                // 构造
                config.form = window._.merge({
                    props: {
                        labelWidth: '140px',
                        labelPosition: 'right',
                        size: 'medium',
                    },
                }, config.form)

                // 简化操作，优先级：fields > parts > part
                if (config.fields) {
                    config.parts = [{
                        fields: config.fields,
                    }]
                } else if (config.part && !config.parts) {
                    config.parts = [config.part]
                }

                // 提交相关
                if (!this.design) {
                    config.operates = config.operates || {}
                    let request = null
                    if (this.isCreateMode) {
                        request = config.create || {
                            text: '新增',
                        }
                    } else {
                        request = config.edit || {
                            text: '保存',
                        }
                    }

                    // 添加一个默认的按钮，
                    config.operates['submit'] = request.url
                        ? {
                            type: 'el-button',
                            text: request.text || '保存',
                            props: {
                                type: 'primary',
                            },
                            listeners: {
                                click: this.onSubmit.bind(this, request),
                            },
                        }
                        : {
                            type: 'el-button',
                            text: request.text, // 等价于innerText
                            props: {
                                type: 'primary',
                            },
                            listeners: {
                                click() {
                                    this.$refs['form'].validate(valid => {
                                        if (!valid) return
                                        alert('未配置submit选项')
                                    })
                                },
                            },
                        }


                    if (config.cancel && config.cancel.to) {
                        let newCancel = null

                        let to = config.cancel.to
                        let type = typeof config.cancel.to
                        switch (type) {
                            case 'string':
                                newCancel = function () {
                                    this.$router.replace(to)
                                }
                                break
                            case 'function':
                                newCancel = to
                                break
                        }

                        newCancel && (this.onCancel = newCancel.bind(this))
                    }
                }

                return config
            },
            // 复用，尽量新增、编辑数据格式一样（编辑时数据中有ID）
            onSubmit(request) {
                this.$refs['form'].validate(valid => {
                    if (!valid) return
                    const payload = this.model

                    return Promise.resolve(request.beforeAjax instanceof Function ? request.beforeAjax(payload) : payload)
                        .then($payload => {
                            if ($payload === false) return
                            $payload = $payload || payload // 不返回值，代指直接更改了引用
                            this.http[request.url.method](this.parseUrl(request.url), $payload).then(res => {
                                if (res.code != 200) return this.$message.error(res.message)

                                this.$message.success((request.text || '操作') + '成功')

                                // 或者其他方式
                                bus.$emit('closeTab', this.$route.fullPath)
                                // this.onCancel()
                            })
                        })
                })
            },

            // 默认返回上一页，会在构造config时重写
            onCancel() {
                this.$router.back()
            },
        },
        async created() {
            // 获取配置，例如正则、内置的校验器
            this.sys = await this.getSys()

            // 设置参数，例如mid、cid（允许外部传入了，正常的话应该路由设置props:true）
            this.setUrlParams()
            this.setState()

            let model = null
            let rawConfig = null

            // 生成config
            if (this._config) {
                rawConfig = this._config
            } else {
                rawConfig = await this.getRawConfig()
                if (!rawConfig) throw '获取表单配置失败,重定向404'
            }
            this.config = this.genConfig(rawConfig)

            // 生成model，需要依赖config
            if (this._model) {
                model = this._model
            } else if (this.mid) {
                model = await this.getFormData()
            }
            this.model = this.genModelStructByConfig(model)

            // # end
            this.$forceUpdate()
            this.$emit('init')
        },
    }
</script>

<style lang="scss" scoped>
    .preview-mode {
        &, .content-wrapper {
            margin: 0;
            padding: 0;
        }

        /deep/ {
            .el-form {
                width: 100% !important;
                pointer-events: none;
            }
        }
    }

    /deep/ {


        .el-form-item__label:after {
            content: "：";
        }

        .el-form-item {
            margin-bottom: 18px !important; // 使用small模式的样式
        }

        .el-form-item.is-required .el-form-item__label:before {
            display: none;
        }

        .el-form-item.is-required > .el-form-item__label:before {
            content: '*';
            color: #f56c6c;
            margin-right: 4px;
            display: inline-block;
        }

        .label-none > .el-form-item__content {
            margin-left: 0 !important;
        }

        .el-input-number input {
            text-align: left;
        }

        // 这里是item组的样式
        .dynamic-form-item-group-wrapper {
            .el-form-item:not(.is-required) {
                margin-bottom: 6px !important;
            }
        }

        // radio相关
        .el-radio, .el-checkbox {
            margin-left: 0;

            &:not(:last-child) {
                margin-right: 30px;
            }
        }

        // checkbox相关
        /*.el-checkbox-group { // 使换行时，显示效果与radio一致*/
        /*    line-height: 1;*/
        /*}*/


        // wang富文本，工具栏换行
        .w-e-toolbar {
            flex-wrap: wrap !important;
        }

        // 日期相关
        .el-date-editor {
            width: 100%;
        }


    }

</style>
